package com.sz.app.oss.api.pay;

import com.sz.app.oss.constants.Permissions;
import com.sz.biz.app.web.BaseController;
import com.sz.biz.common.constants.ErrorCodes;
import com.sz.biz.common.constants.MessagesCodes;
import com.sz.biz.pay.account.dto.PayAccountDto;
import com.sz.biz.pay.account.dto.PaySecurityDto;
import com.sz.biz.pay.account.dto.PaySystemAccountDto;
import com.sz.biz.pay.account.entity.Account;
import com.sz.biz.pay.account.service.AccountService;
import com.sz.biz.pay.constants.PayConstant;
import com.sz.biz.pay.exception.LockPayAccountException;
import com.sz.biz.pay.exception.RetryPayPasswordException;
import com.sz.common.base.cache.redis.RedisPoolManager;
import com.sz.common.base.dto.QResultDto;
import com.sz.common.base.dto.ResultDto;
import com.sz.common.base.exception.Exceptions;
import com.sz.common.base.utils.StringUtils;
import com.sz.common.core.service.PrincipalUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Function: TODO: ADD FUNCTION <br>
 * Author: Johnboy <br>
 * Date: 2017-11-21 16:38:00
 */
@RestController
@RequestMapping("/api/v1/pay")
@Api(description = " ", tags = "048、物流支付")
public class PayController extends BaseController {
    @Autowired
    private AccountService accountService;

    @Value("${sysPayLockTime}")
    private String sysPayLockTimeKey;

    private static String redisKey = "checkPayPWD";

    @ApiOperation(value = "修改密码", notes = "修改物流运营账户密码")
    @ApiImplicitParam(name = "securityDto", value = "修改密码参数信息\n"+
            "newPassword(新密码),repeatPassword(重复密码)", required = true, dataType = "PaySecurityDto", paramType = "body")
    @RequiresPermissions(Permissions.BIZ_MGT_BASE_PAYMENT_PASSWORD)
    @RequestMapping(value = "/password", method = RequestMethod.PUT)
    public ResultDto changePwd(@RequestBody PaySecurityDto securityDto) {
        Jedis jedis = RedisPoolManager.getJedis();
        if (!jedis.exists(PrincipalUtils.getAccountId() + redisKey)) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNTID_EXPIRED);
        }
        Account account = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM).get(0);
        if (null == account) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        showLockTime(account.getId());
        accountService.resetPassword(securityDto.getNewPassword(), securityDto.getRepeatPassword(), account.getId());
        jedis.del(PrincipalUtils.getAccountId() + redisKey);
        jedis.close();
        return new ResultDto();
    }

    @ApiOperation(value = "判断是否需要设置密码", notes = "是否没有密码")
    @RequiresPermissions(value = {Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE, Permissions.BIZ_MGT_BASE_PAYMENT_PASSWORD},
            logical = Logical.OR)
    @RequestMapping(value = "/check/needpassword", method = RequestMethod.GET)
    public ResultDto isNeedSetPWd() {
        List<Account> accounts = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM);
        if (null == accounts || accounts.isEmpty()) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        Account account = accounts.get(0);
        boolean result = accountService.isNeedSetPassword(account.getId());
        ResultDto resultDto = new ResultDto();
        resultDto.setData(result);
        return resultDto;
    }

    @ApiOperation(value = "设置初始密码", notes = "设置初始密码")
    @ApiImplicitParam(name = "paySecurityDto", value = "设置初始密码参数信息\n" +
            "newPassword(新密码)", required = true, dataType = "PaySecurityDto", paramType = "body")
    @RequiresPermissions(Permissions.BIZ_MGT_BASE_PAYMENT_PASSWORD)
    @RequestMapping(value = "/init/password", method = RequestMethod.PUT)
    public ResultDto initPwd(@RequestBody PaySecurityDto paySecurityDto) {
        List<Account> accounts = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM);
        if (null == accounts || accounts.isEmpty()) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        Account account = accounts.get(0);
        boolean result = accountService.isNeedSetPassword(account.getId());
        if (result) {
            accountService.setPassword(paySecurityDto.getNewPassword(), account.getId());
        }
        else {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_PASSWORD_EXISTS);
        }
        return this.setPayPwdSuccessDto();
    }

    @ApiOperation(value = "验证密码", notes = "验证密码")
    @ApiImplicitParam(name = "paySecurityDto", value = "验证密码参数信息\n" +
            "oldPassword(旧密码)", required = true, dataType = "PaySecurityDto", paramType = "body")
    @RequiresPermissions(value = {Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE, Permissions.BIZ_MGT_BASE_PAYMENT_PASSWORD},
            logical = Logical.OR)
    @RequestMapping(value = "/check/password", method = RequestMethod.POST)
    public ResultDto checkPWD(@RequestBody PaySecurityDto paySecurityDto) {
        List<Account> accounts = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM);
        if (null == accounts || accounts.isEmpty()) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        Account account = accounts.get(0);
        try {
            Jedis jedis = RedisPoolManager.getJedis();
            boolean flag = accountService.checkOldPassword(paySecurityDto.getOldPassword(), account.getId());
            if (flag) {
                if (!jedis.exists(PrincipalUtils.getAccountId() + redisKey)) {
                    jedis.setnx(PrincipalUtils.getAccountId() + redisKey, "checkPWD");
                    jedis.expire(PrincipalUtils.getAccountId() + redisKey, 60 * 3);
                }
            }
            jedis.close();
            ResultDto resultDto = new ResultDto();
            resultDto.setData(flag);
            return resultDto;
        } catch (LockPayAccountException e) {
            showLockTime(account.getId());
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_PASSWORD_ERROR_FIVE_TIMES);
        } catch (RetryPayPasswordException e) {
            int leftInputCount = (int) SecurityUtils.getSubject().getSession().getAttribute("PayleftInputCount");
            ResultDto resultDto = ResultDto.createResult(ErrorCodes.ERROR_PAY_EXCESSIVE_ATTEMPTS_LEFT_INPUT_COUNT, leftInputCount);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("leftInputCount", leftInputCount);
            resultDto.setData(dataMap);
            return resultDto;
        }
    }

    @ApiOperation(value = "物流账户查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "q", value = "查询的关键字,账户名称,编码或公司名称",
                    dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "isAsc", value = "排序类型,true为升序，false为降序", required = false,
                    dataType = "boolean", paramType = "query"),
            @ApiImplicitParam(name = "pageIndex", value = "分页 默认第一页",
                    dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "页面大小",
                    dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "userType", value = "客户类型：1是物流，2是贸易， 3两者都是",
                    dataType = "int", paramType = "query")
    })
    @RequiresPermissions(Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE)
    @RequestMapping(value = "/search", method = RequestMethod.GET)
    public QResultDto search(@RequestParam(required = false, defaultValue = "false") boolean isAsc,
                             @RequestParam(defaultValue = "1") int pageIndex,
                             @RequestParam(name = "pageSize", defaultValue = "10") int pageSize,
                             @RequestParam(defaultValue = "") String q) {
        q = StringUtils.replaceSqlPattern(q);
        return accountService.find(q, isAsc, pageIndex, pageSize, 1, PayConstant.PaymentType.SYSTEM,PayConstant.AccountType.LOGISTICS_SYS);
    }

    @ApiOperation(value = "通过AccountId來查询余额信息")
    @ApiImplicitParam(name = "id",value = "id",required = true,dataType = "String",paramType = "body")
    @RequiresPermissions(Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE)
    @RequestMapping(value = "/search/{id}", method = RequestMethod.GET)
    public ResultDto searchByAccountId(@PathVariable String id) {
        ResultDto resultDto = new ResultDto();
        List<PayAccountDto> payAccountDto = accountService.findPayAccountDtoByIdAndThirdType(id, PayConstant.PaymentType.SYSTEM,PayConstant.AccountType.LOGISTICS_SYS);
        if (payAccountDto != null && payAccountDto.size() > 0) {
            resultDto.setData(payAccountDto.get(0));
        }
        return resultDto;
    }

    @ApiOperation(value = "充值")
    @ApiImplicitParam(name = "paySystemAccountDto",value = "充值参数信息\n"+
            "password(密码),thirdId(第三方账户Id),totalBalance(总金额),transactionType(交易类型),transactionBalance(交易金额)",required = true,dataType = "paySystemAccountDto",paramType = "body")
    @RequiresPermissions(Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE)
    @RequestMapping(value = "/pay", method = RequestMethod.POST)
    public ResultDto pay(@RequestBody PaySystemAccountDto paySystemAccountDto) {
        Account account = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM).get(0);
        if (null == account) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        showLockTime(account.getId());
        try {
            accountService.sysRecharge(paySystemAccountDto, account.getId());
        } catch (LockPayAccountException e) {
            showLockTime(account.getId());
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_PASSWORD_ERROR_FIVE_TIMES);
        } catch (RetryPayPasswordException e) {
            int leftInputCount = (int) SecurityUtils.getSubject().getSession().getAttribute("PayleftInputCount");
            ResultDto resultDto = ResultDto.createResult(ErrorCodes.ERROR_PAY_EXCESSIVE_ATTEMPTS_LEFT_INPUT_COUNT, leftInputCount);
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("leftInputCount", leftInputCount);
            resultDto.setData(dataMap);
            return resultDto;
        }
        ResultDto resultDto = getOperateSuccessDto(MessagesCodes.MODULE_PAY_ADD_BALANCE_SUCCESS);
        return resultDto;
    }

    private void showLockTime(String accountId) {
        Jedis jedis = RedisPoolManager.getJedis();
        if (accountService.checkAccountIsLocked(accountId)) {
            if (jedis.exists(sysPayLockTimeKey + accountId + PrincipalUtils.getAccountId())) {
                String times = jedis.get(sysPayLockTimeKey + accountId + PrincipalUtils.getAccountId());
                jedis.close();
                throw Exceptions.bizException(ErrorCodes.ERROR_PAY_FUNCTION_LOCK, times);
            }
            jedis.close();
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_PASSWORD_ERROR_FIVE_TIMES);
        }
    }

    @ApiOperation(value = "当前账户是否已经被锁定")
    @RequiresPermissions(value = {Permissions.BIZ_MGT_BASE_CUSTOMER_BALANCE, Permissions.BIZ_MGT_BASE_PAYMENT_PASSWORD},
            logical = Logical.OR)
    @RequestMapping(value = "/islocked", method = RequestMethod.POST)
    public ResultDto isLocked() {
        Account account = accountService.findByAccountType(PayConstant.AccountType.LOGISTICS_PLATFORM).get(0);
        if (null == account) {
            throw Exceptions.bizException(ErrorCodes.ERROR_PAY_ACCOUNT_NOT_EXISTS);
        }
        showLockTime(account.getId());
        ResultDto resultDto = new ResultDto();
        resultDto.setData(false);
        return resultDto;
    }
}
